
// === uuMeta.ajax ===
// depend: uuMeta, uuMeta.url, uuMeta.date
/*
uuMeta.ajax(url, callback = void 0,
            param = { data, header, timeout, ifmod, ngcallback })
uuMeta.ajax.sync(url) - return response text or ""
uuMeta.ajax.create() - return XHR object
 */
(function uuMetaAjaxScope() {
var _mm = uuMeta,
    _syncxhr, // lazy, xhr object for sync
    _cache = {}; // { "url": last modified date time(unit: ms), ... }

// uuMeta.ajax - Async "GET" or "POST" request
//    uuMeta.ajax("http://example.com/a.php", function(txt) {}, { ifmod: 1 });
//
//        Hash: { data: null,
//                header: ["X-Requested-With", "XMLHttpRequest"],
//                timeout: 10000, ifmod: false, ngcallback: void 0 }
//
//        Mix: date, request data
//        Array: header, request header(ordered)
//                  [(key, value), (key, value), ...]
//        Number: timeout, request timeout
//        Boolean: ifmod, if true is request
//                    with "If-Modified-Since" header
//        Function: ngcallback, ngcallback(state, url)
//
function ajax(url,      // @param URLString: request url
              callback, // @param Function(= undefined): callback function
              param) {  // @param Hash(= undefined):
                        //     { data, header, timeout, ifmod, ngcallback }
  url = _mm.url.abs(url);
  param = param || {};

  var xhr, run = 0, v, i = 0,
      timeout = param.timeout || 10000,
      header = param.header || [],
      ifmod = param.ifmod,
      data = param.data,
      ng = param.ngcallback;

  function state() {
    var ctype, last;

    if (xhr.readyState === 4) {
      if (xhr.status === 200 || !xhr.status) {
        if (callback && !run++) {
          ctype = xhr.getResponseHeader("content-type") || "";
          callback(ctype.indexOf("xml") < 0 ? xhr.responseText
                                            : xhr.responseXML, url);
        }
        if (ifmod) { // parse "Last-Modified" value
          last = xhr.getResponseHeader("Last-Modified");
          _cache[url] = last ? Date.parse(last) : 0;
        }
      } else {
        (ng && !run++) && ng(xhr.status, url); // 304 too
      }
      // gc
      xhr.onreadystatechange = "";
      xhr = null;
    }
  }

  if ( (xhr = createObject()) ) {
    try {
      xhr.onreadystatechange = state;
      xhr.open(data ? "POST" : "GET", url, true);
      // set request header
      header.push("X-Requested-With", "XMLHttpRequest");
      (ifmod && url in _cache) &&
          header.push("If-Modified-Since", _mm.date.toGMTString(_cache[url]));
      data &&
          header.push("Content-Type", "application/x-www-form-urlencoded");

      while ( (v = header[i++]) ) {
        xhr.setRequestHeader(v, header[i]);
        ++i;
      }
      xhr.send(data || null);

      setTimeout(function() { // watchdog timer
        xhr.abort && xhr.abort();
        (ng && !run++) && ng(408, url); // 408 "Request Time-out"
      }, timeout);
      return;
    } catch (err) {}
  }
  (ng && !run++) && ng(400, url);
}

// uuMeta.ajax.sync - Sync "GET" request
function sync(url) { // @param String: request url
                     // @return String: responseText or ""
  try {
    !_syncxhr && (_syncxhr = createObject());

    _syncxhr.open("GET", _mm.url.abs(url), false); // sync
    _syncxhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    _syncxhr.send(null);

    if (_syncxhr.status === 200 || !_syncxhr.status) {
      return _syncxhr.responseText;
    }
  } catch(err) {}
  return "";
}

// uuMeta.ajax.create - create XMLHttpRequest object
function createObject() { // @return XMLHttpRequestObject/undefined:
  var rv;

  try {
    if (_mm.ua.ie && ActiveXObject) {
      rv = new ActiveXObject("Microsoft.XMLHTTP");
    }
    if (!rv && XMLHttpRequest) {
      rv = new XMLHttpRequest();
    }
  } catch (err) {}
  return rv;
}

// uuMeta.ajax.expire - expire [Async If-Modified-Since request] cache
function expire() {
  _cache = {};
}

// --- initialize ---

// --- export ---
_mm.ajax = ajax;
ajax.sync = sync;
ajax.create = createObject;
ajax.expire = expire;

})(); // uuMeta.ajax scope

